﻿using System.Collections.Concurrent;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using TrackSystem.Entities.Extensions;
using TrackSystem.Entities.Models;
using TrackSystem.IO.Alarm;
using TrackSystem.IO.Formula;
using TrackSystem.IO.Models;
using TrackSystem.IO.Sensors;

namespace TrackSystem.IO.Ports
{
    /// <summary>
    /// 端口管理类  管理单个端口的通信
    /// </summary>
    public class PortItem
    {
        #region  公有成员定义
        /// <summary>
        /// 唯一标识符
        /// </summary>
        public Guid ID { get; set; }
        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; }
        
        /// <summary>
        /// 属性信息
        /// </summary>
        public string Attr { get; set; }
        /// <summary>
        /// 协议名称
        /// </summary>
        public ProtocolEnum Protocol { get; set; }
        /// <summary>
        /// 描述
        /// </summary>
        public string Description { get; set; }
        /// <summary>
        /// 是否是能
        /// </summary>
        public bool Enabled { get; set; }
        /// <summary>
        /// 通信周期设定
        /// </summary>
        public int Interval { get; set; }
        /// <summary>
        /// 当前是否处于连接状态
        /// </summary>
        public bool Connected { get; protected set; }
        /// <summary>
        /// 目前是否在运行状态
        /// </summary>
        public bool Running { get; protected set; }
        /// <summary>
        /// 当前成功次数
        /// </summary>
        public int nSucc { get; protected set; }
        /// <summary>
        /// 当前失败次数
        /// </summary>
        public int nFail { get; protected set; }
        /// <summary>
        /// 循环周期时间ms
        /// </summary>
        public int Cycle { get; protected set; }
        /// <summary>
        /// 运行时间消耗时间ms
        /// </summary>
        public int Lapse { get; protected set; }

        /// <summary>
        /// 端口类型
        /// </summary>
        public PortCategory Type { get; protected set; }
        #endregion

        private PortBase port;
        public ConcurrentDictionary<string, ProtocolBase> Sensors;
        private Thread thd;

        public PortItem(ChannelTable channel) 
        {
            ID = Guid.NewGuid();
            Name = channel.Name ??= "None";
            Description = channel.Description ??= "None";
            Interval = channel.Interval;
            Type = channel.PortCategory;

            Attr = channel.PortCategory switch
            {
                PortCategory.TCP => $"TCP: {channel.Ip}:{channel.Port},",
                PortCategory.UDP => $"UDP: {channel.Ip}:{channel.Port},",
                _ => $"串口: {channel.COMn}, {channel.Baudrate}bps, {channel.SerialParams},",
            };
            Protocol = channel.Protocol;

            // 打开端口
            port = new PortBase();
            port.Initialize(channel);

            Sensors = new ();
            Running = true;
            Enabled = true;
            thd = new Thread(WorkerThread);
            thd.IsBackground = true;
            thd.Start();
        }

        public void Disposable()
        {
            Running = false;
            thd.Join();
        }


        /// <summary>
        /// 插入传感器
        /// </summary>
        /// <param name="snr"></param>
        public void InsertSensor(ProtocolBase snr)
        {
            Sensors.TryAdd(snr.Name, snr);
        }

        /// <summary>
        /// 移除传感器
        /// </summary>
        /// <param name="Name"></param>
        /// <returns></returns>
        public bool Remove(string Name)
        {
            return Sensors.TryRemove(Name, out _);
        }

        /// <summary>
        /// 工作者线程,处理端口通信
        /// </summary>
        private void WorkerThread()
        {
            var rxBuffer = new byte[1024];
            var lastTicks = Environment.TickCount;
            var nContinuousWrite = 0;    // 连续写的次数

            while (true)
            {
                var bNeedAcc = false;

                #region 端口操作  ----------------------- ---------------------------------
                try
                {
                    // ---- 端口关闭和退出程序，一定要放在前面，否则会发生不能退出的问题。
                    if (!Enabled || !Running)
                    {
                        if (port.Connected)
                        {
                            port.Close();
                        }

                        if (!Running) // 退出通信程序
                        {
                            break;
                        }
                    }

                    // -----端口启动------------
                    if (Enabled)
                    {
                        if (!port.Connected)
                        {
                            port.Open();

                            // 重新连接后，每个传感器需要初始化
                            foreach (var item in Sensors.Values)
                            {
                                item.NeedInit = true;
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    AlarmFactory.Instance.Insert(ex.Message, AlarmCategory.COMM, Name, 0);
                }
                #endregion

                #region 定时通信任务 -------------------------------------------------------
                var commTicks = Environment.TickCount;
                if (port.Connected)
                {
                    foreach (var ptl in Sensors.Values)
                    {
                        try
                        {
                            // 传感器未使能，不通信
                            if (!ptl.Enabled)
                                continue;

                            // 首先失败次数加1，如果成功则减去这个1
                            // ptl.Fail_R++;

                            // 写入优先处理  连续写操作超过10次，必须更新一次只读寄存器的内容
                            byte[] buffer;
                            int nRecvLen;
                            bool bWritten;
                            if (ptl.WriteFlag && nContinuousWrite < 10 && !ptl.NeedInit)
                            {
                                nContinuousWrite++;      // 连续写计数器加一
                                buffer = ptl.GenerateWriteFrame(out nRecvLen);
                                bWritten = true;
                            }
                            else
                            {
                                nContinuousWrite = 0;    // 发生一次读操作，这该计数器清零
                                buffer = ptl.GenerateReadFrame(out nRecvLen);
                                bWritten = false;
                            }

                            if (buffer != null)
                            {
                                port.Write(buffer);
                                if (nRecvLen == 0 & bWritten)
                                { // 只发不收的情况
                                    ptl.ProcessWriteFrame(buffer);
                                }
                            }

                            if (nRecvLen > 0)
                            {
                                buffer = new byte[1024];
                                var trys = 0;
                                var curPos = 0;

                                // 尝试5次接收到合适长度的数据帧
                                while (curPos < nRecvLen && trys++ < 10)
                                {
                                    Thread.Sleep(10);
                                    var rxLen = port.Read(rxBuffer);
                                    if (rxLen == 0) break;                 // 端口无数据，直接退出该循环
                                    for (int i = 0; i < rxLen; i++)
                                        buffer[curPos++] = rxBuffer[i];
                                }

                                // 收到数据，则交给具体的通信处理程序进行处理，有的协议长度不定，期望收到的长度可以定义的长，在帧的协议程序中进行处理
                                if (curPos > 0)
                                {
                                    if (bWritten)
                                        ptl.ProcessWriteFrame(buffer[0..curPos]);
                                    else
                                        ptl.ProcessReadFrame(buffer[0..curPos]);
                                }

                                // 监测是否还有需要写入的数据
                                if (ptl.WriteFlag)
                                    bNeedAcc = true;
                            }
                        }
                        catch (SocketException ex)  // SOCKET 端口断开，需要重新连接
                        {
                            AlarmFactory.Instance.Insert($"{ex.Message} Code:{ex.ErrorCode}", AlarmCategory.COMM, Name, 0);
                            
                            // 该错误为长时间没有收到返回信息，不需要重启端口
                            if (ex.ErrorCode == 10060)
                            {
                                continue;
                            }

                            // TCP模式需要判断是否是端口关闭
                            if (port.NeedReset())
                            {
                                port.Close();
                                break;
                            }

                            //需要重新初始化
                            ptl.NeedInit = true;
                        }
                        catch (Exception ex)
                        {
                            AlarmFactory.Instance.Insert(ex.Message, AlarmCategory.COMM, Name, 0);
                            if (port.type == PortCategory.SRIAL)
                            {
                                // 需要重启端口
                                port.Close();
                            }
                        }
                    }
                }
                #endregion

                #region 时间计算------------------------------------------------------------
                var temp = Environment.TickCount;
                Cycle = temp - lastTicks;
                Lapse = temp - commTicks; //startTicks;
                Connected = port.Connected;
                lastTicks = temp;
                var deltaTicks = Interval - Lapse;

                // 缓冲区有待写入的数据，则取消休眠延时
                if (deltaTicks > 0 && !bNeedAcc)
                {
                    Thread.Sleep(deltaTicks);
                }
                #endregion
            }            
        }

        /// <summary>
        /// 获取端口属性值
        /// </summary>
        /// <param name="idx"></param>
        /// <returns></returns>
        public OperandDef GetVal(int idx)
        {
            switch (idx)
            {
                case 0: return new OperandDef(Enabled);
                case 1: return new OperandDef(port.Connected);
                case 2: return new OperandDef(Cycle);
                case 3: return new OperandDef(Lapse);
            }
            return new OperandDef();
        }
    }
}
